﻿using System;
using System.ComponentModel.DataAnnotations;
using RepositoryFramework.Exceptions;
using RepositoryFramework.Validators;

namespace RepositoryFramework.Services
{
	/// <summary>
	/// Class <see cref="ServiceBase" /> define base service class
	/// </summary>
	/// <typeparam name="P">The type of the unit of work.</typeparam>
	/// <typeparam name="TEntity">The type of the validatior.</typeparam>
	public abstract class ServiceBase<P, U, T> : IServiceBase<T>
		where T : class
		where P : IUnitOfWorkProvider<U>
		where U : IUnitOfWork
	{
		#region Fields

		private readonly IValidator<T> _validator;
		private readonly P _unitOfWorks;

		private U _readOnly = default(U);
		#endregion Fields

		#region .ctor

		/// <summary>
		/// Class <see cref="UserService" /> constructor
		/// </summary>
		/// <param name="unitOfWorks">The unit of works.</param>
		/// <param name="validator">The validator.</param>
		/// <exception cref="System.ArgumentNullException">
		/// unitOfWorkProvider
		/// or
		/// validator
		/// </exception>
		public ServiceBase(P unitOfWorks, IValidator<T> validator)
		{
			if (unitOfWorks == null)
			{
				throw new ArgumentNullException("unitOfWorks");
			}
			if (validator == null)
			{
				throw new ArgumentNullException("validator");
			}
			_validator = validator;
			_unitOfWorks = unitOfWorks;
		}

		#endregion .ctor

		#region Methods

		/// <summary>
		/// Gets the read-only unit of work.
		/// </summary>
		/// <returns></returns>
		protected U GetTransactionalUnitOfWork()
		{
			var unitOfWork = _unitOfWorks.GetReadOnly();
			if (unitOfWork == null)
			{
				throw new ArgumentNullException("transactional only unit of work");
			}
			return unitOfWork;
		}

		/// <summary>
		/// Gets the read-only unit of work.
		/// </summary>
		protected U GetReadOnlyUnitOfWork()
		{
			if (_readOnly == null)
			{
				_readOnly = _unitOfWorks.GetReadOnly();
				if (_readOnly == null)
				{
					throw new ArgumentNullException("read only unit of work");
				}
			}
			return _readOnly;
		}

		/// <summary>
		/// Validates the specified entity.
		/// </summary>
		/// <param name="entity">The entity.</param>
		protected void Validate(T entity)
		{
			var result = _validator.Validate(entity);
			if (!result.IsValid)
			{
				throw new CustomValidationException(result.Errors);
			}
		}

		/// <summary>
		/// Validates the specified entity.
		/// </summary>
		/// <param name="entity">The entity.</param>
		/// <returns>True if not null and valid</returns>
		public bool IsValid(T entity)
		{
			if (entity == null)
			{
				return false;
			}
			var result = _validator.Validate(entity);
			return result.IsValid;
		}

		#endregion Methods
	}
}